Esta es más o menos una pregunta sobre metodología y justificación que cualquier otra cosa. Al programar varios módulos del núcleo para Linux, me confunde lo que considero una forma torpe de diseñar funciones. Por ejemplo, para recuperar el inodo de un archivo dada su ruta, tuve que usar algo como:
struct inode *inode; struct path path; kern_path(path_name, LOOKUP_FOLLOW, &path); inode = path.dentry->d_inode;¿Por qué no solo una función que funciona como:
struct inode inode; struct path path = kern_path(path_name, LOOKUP_FOLLOW); inode = path.dentry->d_inode;Parece mucho más intuitivo.
¿Y qué harías con el int que devuelve kern_path ?
Es importante que las funciones puedan devolver algún tipo de código de error para que el usuario pueda asegurarse de que la función se realizó correctamente. Hay dos opciones obvias:
Devuelve el código de error.
Esto significa que debe tomar el otro valor que desea devolver como parámetro.
Devolver el valor.
Esto significa que debe tomar el código de error como parámetro.
Dado que solo puede devolver 1 valor en C, debe tomar algo como parámetro, ya que en última instancia desea devolver dos cosas al usuario (el código de error y el valor).
El kernel es un programa en C con restricciones especiales. En particular, no se permite que la pila de llamadas sea profunda (IIRC, está limitada a 4 Kbytes).
Cuando devuelves una struct , la ABI (ver la ABI x86-64 ...) ordena que (excepto por algunas struct cortas que se ajustan en dos palabras) la struct devuelta pase por la pila. Por lo tanto, dicho estilo favorecería los marcos de pila bastante grandes, por lo que cumpliría más fácilmente con el límite de pila.
Por cierto, el estilo habitual sería devolver algún código de error entero y modificar los datos señalados por los punteros de argumento.
En x86-64/Linux, devolver una estructura de dos valores escalares (enteros, punteros, enumeraciones, dobles, ...) definitivamente vale la pena, vea esto .
Eso tiene el beneficio de reducir la copia de bloques de memoria de un lado a otro.
El uso de esta metodología permite que la función invocada solo modifique ciertas partes de la estructura pasada y no se copia memoria en absoluto.